﻿using Haidou.Wpf.Domian.Enums;
using Haidou.Wpf.Util.Settings;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Cache;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Haidou.Wpf.Util
{
	/// <summary>
	/// Web相关方法。
	/// Boundary: 
	/// </summary>
	public static class WebHelper
	{
		private const string Boundary = "+++fringe+++";

		public static string Protect(string str)
		{
			byte[] entropy = Encoding.ASCII.GetBytes(Environment.MachineName);
			return Convert.ToBase64String(ProtectedData.Protect(Encoding.ASCII.GetBytes(str), entropy, DataProtectionScope.CurrentUser));
		}

		public static string Unprotect(string str)
		{
			if (string.IsNullOrWhiteSpace(str))
			{
				return str;
			}

			byte[] entropy = Encoding.ASCII.GetBytes(Environment.MachineName);
			return Encoding.ASCII.GetString(ProtectedData.Unprotect(Convert.FromBase64String(str), entropy, DataProtectionScope.CurrentUser));
		}

		public static string AppendQuery(string url, Dictionary<string, string> args)
		{
			if (args == null)
			{
				return url;
			}

			string suffix = args.Select(s => s.Key + "=" + WebUtility.UrlEncode(s.Value)).Aggregate((p, n) => p + "&" + n);

			return url + (string.IsNullOrWhiteSpace(suffix) ? "" : "?" + suffix);
		}


		public static async Task<string> Get(string url, NameValueCollection headers = null)
		{
			using (WebResponse webResponse = await GetResponse(HttpMethod.Get, url, headers))
			{
				await using (Stream responseStream = webResponse.GetResponseStream())
				{
					if (responseStream == null)
					{
						return null;
					}

					using (StreamReader reader = new(responseStream, Encoding.UTF8))
					{
						return await reader.ReadToEndAsync();
					}
				}
			}
		}

		public static async Task<string> GetNotUsing(string url, NameValueCollection headers = null)
		{
			WebResponse webResponse = await GetResponse(HttpMethod.Get, url, headers);

			Stream responseStream = webResponse.GetResponseStream();

			if (responseStream == null)
			{
				return null;
			}

			StreamReader reader = new(responseStream, Encoding.UTF8);
			return await reader.ReadToEndAsync();


		}


		public static async Task<string> Post(string url, string content, NameValueCollection headers = null)
		{
			using (WebResponse webResponse = await GetResponse(HttpMethod.Post, url, content, "application/json", headers))
			{
				await using (Stream responseStream = webResponse.GetResponseStream())
				{
					if (responseStream == null)
					{
						return null;
					}

					using (StreamReader reader = new(responseStream, Encoding.UTF8))
					{
						return await reader.ReadToEndAsync();
					}
				}
			}
		}

		public static async Task<string> PostMultipart(string url, Dictionary<string, string> args)
		{
			await using (MemoryStream stream = new())
			{
				stream.WriteStringUtf8(GetMultipartString(Boundary, args));

				using (WebResponse webResponse = await GetResponse(HttpMethod.Post, url, stream, "multipart/form-data; boundary=" + Boundary))
				{
					await using (Stream responseStream = webResponse.GetResponseStream())
					{
						if (responseStream == null)
						{
							return null;
						}

						using (StreamReader reader = new(responseStream, Encoding.UTF8))
						{
							return await reader.ReadToEndAsync();
						}
					}
				}
			}
		}

		public static async Task<string> SendFile(string url, Stream data, string filename, Dictionary<string, string> args = null, NameValueCollection headers = null, string streamName = "file")
		{
			await using (Stream head = GetMultipartStream(Boundary, args, filename, data, streamName))
			{
				using (WebResponse webResponse = await GetResponse(HttpMethod.Post, url, head, "multipart/form-data; boundary=" + Boundary, headers))
				{
					await using (Stream responseStream = webResponse.GetResponseStream())
					{
						if (responseStream == null)
						{
							return null;
						}

						using (StreamReader reader = new(responseStream, Encoding.UTF8))
						{
							return await reader.ReadToEndAsync();
						}
					}
				}
			}
		}

		public static async Task<string> SendFile2(string url, Stream data, string filename, Dictionary<string, string> args = null, NameValueCollection headers = null)
		{
			await using (Stream head = GetMultipartStream(Boundary, args, filename, data))
			{
				HttpWebRequest request = GetWebRequest(HttpMethod.Post, url, headers, "multipart/form-data; boundary=" + Boundary, head.Length);

				await using (Stream requestStream = await request.GetRequestStreamAsync())
				{
					requestStream.WriteStream(head);
				}

				using (WebResponse response = await request.GetResponseAsync())
				{
					await using (Stream responseStream = response.GetResponseStream())
					{
						if (responseStream == null)
						{
							return null;
						}

						using (StreamReader reader = new(responseStream, Encoding.UTF8))
						{
							return await reader.ReadToEndAsync();
						}
					}
				}
			}
		}

		public static Stream GetMultipartStream(string border, Dictionary<string, string> args, string filename, Stream data, string streamName = "file")
		{
			MemoryStream stream = new();

			if (args.Any(w => !string.IsNullOrEmpty(w.Key) && !string.IsNullOrEmpty(w.Value)))
			{
				stream.WriteStringUtf8("Content-Type: text/plain; charset=utf-8");
			}

			foreach (KeyValuePair<string, string> content in args.Where(w => !string.IsNullOrEmpty(w.Key) && !string.IsNullOrEmpty(w.Value)))
			{
				stream.WriteStringUtf8($"--{border}\r\nContent-Disposition: form-data; name=\"{content.Key}\"\r\n\r\n{content.Value}\r\n");
			}

			if (!string.IsNullOrWhiteSpace(filename))
			{
				stream.WriteStringUtf8($"--{border}\r\nContent-Disposition: form-data; name={streamName}; filename={filename};\r\n\r\n");
			}
			//stream.WriteStringUtf8($"--{border}\r\nContent-Disposition: form-data; name=\"image\"; filename=\"{filename}\"\r\nContent-Type: image/gif\r\n\r\n"); //TODO: Fixed content type.

			stream.WriteStream(data);

			stream.WriteStringUtf8($"\r\n--{border}--\r\n");

			stream.Position = 0;
			return stream;
		}

		public static string GetMultipartString(string border, Dictionary<string, string> args)
		{
			return args.Where(w => !string.IsNullOrEmpty(w.Key) && !string.IsNullOrEmpty(w.Value))
				.Aggregate("", (p, n) => p + $"--{border}\r\nContent-Disposition: form-data; name=\"{n.Key}\"\r\n\r\n{n.Value}\r\n") + $"--{border}--\r\n";
		}


		public static Task<WebResponse> GetResponse(HttpMethod method, string url, NameValueCollection headers = null)
		{
			try
			{
				return GetWebRequest(method, url, headers).GetResponseAsync();
			}
			catch (WebException we)
			{
				if (we.Response is not WebResponse resp)
				{
					throw;
				}

				return Task.FromResult(resp);
			}
			catch (Exception ex)
			{
				LogWriter.Log(ex, "Get response: " + url);
			}

			return null;
		}

		private static Task<WebResponse> GetResponse(HttpMethod method, string url, string content, string contentType = null, NameValueCollection headers = null)
		{
			try
			{
				HttpWebRequest request = GetWebRequest(method, url, headers, contentType, content.Length);

				if (content.Length > 0)
				{
					using (Stream requestStream = request.GetRequestStream())
					{
						requestStream.WriteStringUtf8(content);
					}
				}

				return request.GetResponseAsync();
			}
			catch (WebException we)
			{
				if (we.Response is not WebResponse resp)
				{
					throw;
				}

				return Task.FromResult(resp);
			}
			catch (Exception ex)
			{
				LogWriter.Log(ex, "Get response: " + url);
			}

			return Task.FromResult<WebResponse>(null);
		}

		private static async Task<WebResponse> GetResponse(HttpMethod method, string url, Stream data, string contentType = null, NameValueCollection headers = null)
		{
			try
			{
				HttpWebRequest request = GetWebRequest(method, url, headers, contentType, data?.Length ?? 0);

				if (request.ContentLength > 0)
				{
					await using (Stream requestStream = await request.GetRequestStreamAsync())
					{
						requestStream.WriteStream(data);
					}
				}

				return await request.GetResponseAsync();
			}
			catch (WebException we)
			{
				if (we.Response is not WebResponse resp)
				{
					throw;
				}

				return resp;
			}
			catch (Exception ex)
			{
				LogWriter.Log(ex, "Get response: " + url);
			}

			return null;
		}


		private static HttpWebRequest GetWebRequest(HttpMethod method, string url, NameValueCollection headers = null, string contentType = null, long contentLength = 0)
		{
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

			if (headers != null)
			{
				request.Headers.Add(headers);
			}

			request.Method = method.ToString();
			request.Proxy = GetProxy();
			request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
			request.ContentType = contentType;


			if (contentLength == 0)
			{
				request.KeepAlive = false;
				return request;
			}

			if (method == HttpMethod.Get)
			{
				request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
			}

			request.AllowWriteStreamBuffering = IsProxyBeingUsed();
			request.ContentLength = contentLength;
			request.Pipelined = false;
			request.Timeout = -1;
			return request;
		}


		public static IWebProxy? GetProxy()
		{
			if (UserSettings.All.ProxyMode == ProxyTypes.System)
			{
				return WebRequest.GetSystemWebProxy();
			}

			return UserSettings.All.ProxyMode == ProxyTypes.Manual
				? string.IsNullOrEmpty(UserSettings.All.ProxyHost) || UserSettings.All.ProxyPort <= 0 ? null :
					new WebProxy($"{UserSettings.All.ProxyHost}:{UserSettings.All.ProxyPort}", true, null, new NetworkCredential(UserSettings.All.ProxyUsername, Unprotect(UserSettings.All.ProxyPassword)))
				: (IWebProxy?)null;
		}

		internal static bool IsProxyBeingUsed()
		{
			if (UserSettings.All.ProxyMode == ProxyTypes.System)
			{
				return true;
			}

			return UserSettings.All.ProxyMode == ProxyTypes.Manual
&& !string.IsNullOrEmpty(UserSettings.All.ProxyHost) && UserSettings.All.ProxyPort > 0;
		}

		//var ip = await GetstringIp();
		//var regionName = GetstringIpAddress(ip);



		public static string Getip()
		{
			string ip = SendRequest("http://pv.sohu.com/cityjson?ie=utf-8");

			return ip;



		}


		// <summary>   
		/// Get方式获取url地址输出内容   
		/// </summary> /// <param name="url">url</param>   
		/// <param name="encoding">返回内容编码方式，例如：Encoding.UTF8</param>   
		public static string SendRequest(string url)
		{
			HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
			webRequest.Method = "GET";
			HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
			StreamReader sr = new(webResponse.GetResponseStream(), Encoding.UTF8);
			return sr.ReadToEnd();
		}



		/// <summary>
		/// 抓取网页查询信息
		/// </summary>
		/// <param name="url"></param>
		/// <param name="encoding">编码类型</param>
		/// <returns></returns>
		public static string GetHtml(string url, string encoding)
		{
			string pagehtml = string.Empty;
			try
			{
				using (WebClient MyWebClient = new())
				{
					//控制台应用和.net core 需要这一句，需要安装NetGet包System.Text.Encoding.CodePages
					Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
					Encoding encode = Encoding.GetEncoding(encoding);
					MyWebClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.84 Safari/537.36");
					MyWebClient.Credentials = CredentialCache.DefaultCredentials;
					Byte[] pageData = MyWebClient.DownloadData(url);
					pagehtml = encode.GetString(pageData);
				}
			}
			catch (Exception)
			{

				return "访问" + url + "失败，请检查网络配置";
			}
			return pagehtml;
		}

		/// <summary>
		/// 根据Ip获取我们所要的信息
		/// </summary>
		/// <param name="strIp"></param>
		/// <returns></returns>
		public static string GetstringIpAddress(string strIp)
		{
			string html = GetHtml("https://www.ip138.com/iplookup.asp?ip=" + strIp + "&action=2", "gb2312");
			string pre = "var ip_result = {\"ASN归属地\":\"";
			int pos = html.IndexOf(pre);
			html = html.Substring(pos + pre.Length);
			html = html.Substring(0, html.IndexOf('"'));

			return html;
		}


		/// <summary>
		/// 根据Ip获取我们所要的信息
		/// </summary>
		/// <param name="strIp"></param>
		/// <returns></returns>
		public static async Task<string> GetstringIp()
		{
			return await GetExternalIPAddressAsync();

		}



		public static async Task<string> GetExternalIPAddressAsync()
		{
			// 使用HttpClient版本（推荐，异步且更现代）
			using (HttpClient client = new())
			{
				HttpResponseMessage response = await client.GetAsync("http://icanhazip.com"); // 或者使用其他提供IP查询服务的网址
				if (response.IsSuccessStatusCode)
				{
					string content = await response.Content.ReadAsStringAsync();
					return Regex.Match(content, @"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}").Value;
				}
				else
				{
					throw new Exception($"无法获取IP地址：HTTP状态码 {response.StatusCode}");
				}
			}
		}



	}
}
